// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

package endpoint

import (
	"fmt"
	"net"
	"net/url"
	"path"
	"strings"
)

type CredsRequirement int

const (
	// CREDS_REQUIRE-此类型连接所需的凭据/证书。
	CREDS_REQUIRE CredsRequirement = iota
	// CREDS_DROP-凭证/证书不需要，应忽略。
	CREDS_DROP
	// CREDS_可选-如果提供了凭证/证书，则可能会使用该凭证/证书
	CREDS_OPTIONAL
)

func extractHostFromHostPort(ep string) string {
	host, _, err := net.SplitHostPort(ep)
	if err != nil {
		return ep
	}
	return host
}

func extractHostFromPath(pathStr string) string {
	return extractHostFromHostPort(path.Base(pathStr))
}

// mustSplit2从字符串返回值。斯普利恩（s，9月2日）。
// 如果找不到sep，则返回（“，”，false）。
func mustSplit2(s, sep string) (string, string) {
	spl := strings.SplitN(s, sep, 2)
	if len(spl) < 2 {
		panic(fmt.Errorf("token '%v' expected to have separator sep: `%v`", s, sep))
	}
	return spl[0], spl[1]
}

func schemeToCredsRequirement(schema string) CredsRequirement {
	switch schema {
	case "https", "unixs":
		return CREDS_REQUIRE
	case "http":
		return CREDS_DROP
	case "unix":
		// 保留以前的行为来自：
		// https:
		// 这可能是由于缺少“故障排除”而导致的错误。
		// 同时，让用户决定他们是否想要凭证控制似乎是合法的（并且“unixs”模式不是标准的东西）。
		return CREDS_OPTIONAL
	case "":
		return CREDS_OPTIONAL
	default:
		return CREDS_OPTIONAL
	}
}

// 此函数将etcd server支持的端点名称转换为
// grpc支持的端点以及附加信息
// （证书验证的服务器名称，Requireds-是否需要证书）。
// 主要区别：
// /-etcd支持unix和https名称，而不是unix和http到
// 区分需要配置证书。
// /-etcd支持http（s）名称，而不是grpc/拨号方法支持的tcp。
// -etcd支持unix:
// （与unix相反：grpc对当前目录文件使用的本地文件规范名）。
// -在unix（s）架构中，不带“端口”（冒号后的内容）的最后一个段（文件名）被视为serverName-以允许对受证书保护的通信进行本地测试。
// 查看更多：
// github.com/grpc/grpc-go/blob/26c143bd5f59344a4b8a1e491e0f5e18aa97abc7/internal/grpcutil/target.go#L47 
// golang.org/pkg/net/#Dial 
// github.com/grpc/grpc/blob/master/doc/naming.md HasPrefix（ep，“unixs:
func translateEndpoint(ep string) (addr string, serverName string, requireCreds CredsRequirement) {
	if strings.HasPrefix(ep, "unix:") || strings.HasPrefix(ep, "unixs:") {
			// 绝对路径案例
			schema, absolutePath := mustSplit2(ep, ":// ”）
			return "unix:// “+绝对路径，extractHostFromPath（绝对路径），SchemeToCredRequest（模式）
		}
		if strings.HasPrefix(ep, "unix:// ”）字符串。HasPrefix（ep，“unixs:
			// 遗留本地路径
			return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema)
		}
		schema, localPath := mustSplit2(ep, ":")
		return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema)
	}

	if strings.Contains(ep, ":// “{
		url, err := url.Parse(ep)
		if err != nil {
			return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL
		}
		if url.Scheme == "http" || url.Scheme == "https" {
			return url.Host, url.Hostname(), schemeToCredsRequirement(url.Scheme)
		}
		return ep, url.Hostname(), schemeToCredsRequirement(url.Scheme)
	}
	// 处理10.0.0.44:437这样的普通地址。
	return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL
}

// requiresRedentials返回给定端点是否需要
// 连接的凭据/证书。
func RequiresCredentials(ep string) CredsRequirement {
	_, _, requireCreds := translateEndpoint(ep)
	return requireCreds
}

// 解释端点解析
// （http | https:
// 并返回要连接到的低级地址（由“net”支持）、
// 以及用于x509证书匹配的服务器名称。
func Interpret(ep string) (address string, serverName string) {
	addr, serverName, _ := translateEndpoint(ep)
	return addr, serverName
}
